package org.qloudgen.borg.pages;

import java.io.IOException;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.ajax.markup.html.form.AjaxButton;
import org.apache.wicket.ajax.markup.html.navigation.paging.AjaxPagingNavigator;
import org.apache.wicket.markup.html.WebMarkupContainer;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.form.Button;
import org.apache.wicket.markup.html.form.Form;
import org.apache.wicket.markup.html.form.FormComponent;
import org.apache.wicket.markup.html.form.Radio;
import org.apache.wicket.markup.html.form.RadioGroup;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.markup.html.list.PageableListView;
import org.apache.wicket.model.CompoundPropertyModel;
import org.apache.wicket.model.Model;
import org.apache.wicket.request.mapper.parameter.PageParameters;
import org.qloudgen.borg.CommonValues;
import org.qloudgen.borg.beans.Cluster;
import org.qloudgen.borg.beans.FuncBean;
import org.qloudgen.borg.beans.ParamBean;
import org.qloudgen.borg.beans.ResultBean;
import org.qloudgen.borg.metadb.DefaultImpl;
import org.qloudgen.borg.pages.common.BasePage;
import org.qloudgen.borg.pages.model.FuncModel;
import org.qloudgen.borg.utils.CommonUtils;
import org.qloudgen.borg.utils.RemoteShellExec;

import com.google.common.collect.Lists;

@SuppressWarnings({"unchecked","rawtypes"})
public class AppFuncListPage extends BasePage{

	/**
	 * @Author youngeryoyo
	 * youngeryoyo@gmail.com
	 */

	static Log log = LogFactory.getLog(AppFuncListPage.class);

	private static final long serialVersionUID = 1L;
	private transient List<FuncBean> funcs = Lists.newArrayList();
	private transient String buttonName;
	private transient String appNo;
	private transient List<ParamBean> params = Lists.newArrayList();
	private transient List<ResultBean> columns = Lists.newArrayList();
	private transient List<Cluster> clusters = Lists.newArrayList();
	private transient List<String> files = Lists.newArrayList();
	private transient String result = null;
	private transient boolean flag = false;
	
	public AppFuncListPage(final PageParameters pageParameters){
		
		super();
		this.pp = pageParameters;
		
		appNo = this.pp.get("appNo").toString();

		// get from metadb
		DefaultImpl.getFuncList(appNo,funcs);
		
		// border and detail
		final WebMarkupContainer border = new WebMarkupContainer("border");
		border.setOutputMarkupId(true);
		this.add(border);
		
		final TextArea<String> detail = new TextArea<String>("detail");
		detail.setOutputMarkupId(true);
		detail.setVisible(false);
		detail.setDefaultModel(new Model<String>(""));
		border.add(detail);
		
		// add form 
		Form<?> form = new Form("form",new CompoundPropertyModel<FuncModel>(new FuncModel())) {
			
			private static final long serialVersionUID = 1L;

			@Override
			protected void onSubmit() {
				
				FuncBean func = ((FuncModel)getDefaultModelObject()).getGroup();
				buttonName = ((FormComponent<String>)findSubmittingButton()).getInputName();
					
				if (func == null && !buttonName.equals("create")) {	info(CommonValues.INFO_NO_SELECT); return; } 
				
				if (buttonName.equals("create")) setResponsePage(AppFuncCreatePage.class,pageParameters);

				if (buttonName.equals("pause")) {

					// update func status
					if (func.getFuncStatus().equals(CommonValues.STATUS_INIT)
							|| func.getFuncStatus().equals(CommonValues.STATUS_PAUSE)){
						DefaultImpl.updateFuncStatus(func.setFuncStatus(func.getFuncStatus().equals(CommonValues.STATUS_INIT)?CommonValues.STATUS_PAUSE:CommonValues.STATUS_INIT));
						setResponsePage(AppFuncListPage.class,pp );
					} 
					
					info(CommonValues.INFO_CAN_NOT_PAUSED);
					return;
					
				} 
				
				if (buttonName.equals("upload")) {

					if (upLoad(func) == -1) { error(CommonValues.ERROR_UPLOAD_FILE); return; }
					
					// update func status
					DefaultImpl.updateFuncStatus(func.setFuncStatus(CommonValues.STATUS_UPLOAD));
					return;
					
				} 
				
				if (buttonName.equals("init")) {
					
					if (flag) { info(CommonValues.INFO_NOT_REPEAT_CLICK); return;}
					
					if (!func.getFuncStatus().equalsIgnoreCase(CommonValues.STATUS_UPLOAD)) { info(CommonValues.INFO_NOT_UPLOAD); return;}
					
					if (init(func) == -1) { error(CommonValues.ERROR_INIT); return; }
					
					// update func status
					DefaultImpl.updateFuncStatus(func.setFuncStatus(CommonValues.STATUS_INIT));
					return;
				} 
				
				if (buttonName.equals("delete")) {
					DefaultImpl.deleteFunc(func.getFuncNo());
					setResponsePage(AppFuncListPage.class,pp );
				}
				
				if (buttonName.equals("modify")) {
					setResponsePage(AppFuncModifyPage.class, new PageParameters().add("funcNo", func.getFuncNo()).add("appNo", appNo) );
				}
			}
		};
		this.add(form);
	
		// add paramContainer/paramGrid
		final WebMarkupContainer paramContainer=new WebMarkupContainer("paramContainer"); 
		paramContainer.setOutputMarkupId(true);
		this.add(paramContainer);
		
		final WebMarkupContainer paramGrid=new WebMarkupContainer("paramGrid"); 
		paramGrid.setOutputMarkupId(true);
		paramGrid.setVisible(false);
		paramContainer.add(paramGrid);
		
		final PageableListView<ParamBean> paramListView =new PageableListView<ParamBean>( "paramRows", params, CommonValues.SHOW_LIST_NUMBER) {

			private static final long serialVersionUID = 1L;

			@Override
			protected void populateItem(ListItem<ParamBean> item) {
				final ParamBean param = (ParamBean) item.getModelObject();
				item.add( new Label( "paramSeq", String.valueOf(param.getParamSeq())));
				item.add( new Label( "paramCode", param.getParamCode()));
				item.add( new Label( "paramName", param.getParamName()));
				item.add( new Label( "paramDesc", param.getParamDesc()));	
				item.add( new Label( "paramType", CommonValues.paramTypes.get(param.getParamType())));			
				
			}
		};
		paramListView.setOutputMarkupId(true);		
		paramGrid.add(paramListView);
		
		// add resultContainer/resultGrid
		final WebMarkupContainer resultContainer=new WebMarkupContainer("resultContainer"); 
		resultContainer.setOutputMarkupId(true);
		this.add(resultContainer);
		
		final WebMarkupContainer resultGrid=new WebMarkupContainer("resultGrid"); 
		resultGrid.setOutputMarkupId(true);
		resultGrid.setVisible(false);
		resultContainer.add(resultGrid);
		
		final PageableListView<ResultBean> resultListView =new PageableListView<ResultBean>( "resultRows", columns, CommonValues.SHOW_LIST_NUMBER) {

			private static final long serialVersionUID = 1L;

			@Override
			protected void populateItem(ListItem<ResultBean> item) {
				final ResultBean column = (ResultBean) item.getModelObject();
				item.add( new Label( "tableName", column.getTableName()));
				item.add( new Label( "columnCode", column.getColumnCode()));
				item.add( new Label( "columnName", column.getColumnName()));
				item.add( new Label( "isOrder", CommonValues.isOrders.get(column.getIsOrder())));				
				
			}
		};
		resultListView.setOutputMarkupId(true);
		resultGrid.add(resultListView);
		
		// add group
		final RadioGroup<?> group=new RadioGroup<Object>("group"); 
		group.setOutputMarkupId(true);
		group.setRenderBodyOnly(false);
		form.add(group);
		
		// add func list
		final PageableListView<FuncBean> listView =new PageableListView<FuncBean>( "rows", funcs, CommonValues.SHOW_LIST_NUMBER) {

			private static final long serialVersionUID = 1L;

			@Override
			protected void populateItem(ListItem<FuncBean> item) {
				final FuncBean func = (FuncBean) item.getModelObject();
				item.add(new Radio<FuncBean>("radio", new Model<FuncBean>(func)));
				item.add( new Label( "id", func.getFuncNo()));
				item.add( new Label( "name", func.getFuncName()));
				item.add( new Label( "desc", func.getFuncDesc()));
				item.add( new Label( "status", CommonValues.statusNames.get(func.getFuncStatus())));
				item.add( new Label( "app", func.getAppName()));
				item.add(  new AjaxLink("tablefile"){

					private static final long serialVersionUID = 1L;

					@Override
					public void onClick(AjaxRequestTarget target) {
						detail.setDefaultModelObject(func.getTableFile());
						paramGrid.setVisible(false);
						resultGrid.setVisible(false);
						detail.setVisible(true);
						target.add(paramContainer);
						target.add(resultContainer);
						target.add(border);
						
					}});
				item.add( new AjaxLink("handlerfile"){

					private static final long serialVersionUID = 1L;

					@Override
					public void onClick(AjaxRequestTarget target) {
						detail.setDefaultModelObject(func.getHandlerFile());
						paramGrid.setVisible(false);
						resultGrid.setVisible(false);
						detail.setVisible(true);
						target.add(paramContainer);
						target.add(resultContainer);
						target.add(border);
						
					}});
				
				Label count = new Label("count",func.getParamCount());
				
				AjaxLink param = new AjaxLink("paramcount"){

					private static final long serialVersionUID = 1L;

					@Override
					public void onClick(AjaxRequestTarget target) {

						DefaultImpl.getFuncParameterList(func.getFuncNo(), params);
						paramGrid.setVisible(true);
						resultGrid.setVisible(false);
						detail.setVisible(false);
						target.add(paramContainer);
						target.add(resultContainer);
						target.add(border);
					}};
				param.add(count);
				item.add(param );
				
				AjaxLink result = new AjaxLink("resulttable"){

					private static final long serialVersionUID = 1L;

					@Override
					public void onClick(AjaxRequestTarget target) {
						DefaultImpl.getFuncColumnList(func.getFuncNo(), columns);
						paramGrid.setVisible(false);
						resultGrid.setVisible(true);
						detail.setVisible(false);
						target.add(paramContainer);
						target.add(resultContainer);
						target.add(border);
					}
				};
					
				item.add(result );
				
			}
		};
		listView.setOutputMarkupId(true);
		group.add(listView);
		
		// add navigator
		final AjaxPagingNavigator nav = new AjaxPagingNavigator("navigator", listView);
		nav.setOutputMarkupId(true);
		group.add(nav);
		
		// add button 
		form.add(new Button("create"));
		form.add(new Button("delete"));
		form.add(new Button("pause"));
		form.add(new AjaxButton("upload"){
			private static final long serialVersionUID = 1L;
			protected void onAfterSubmit(AjaxRequestTarget target, Form<?> form) {
				
				if (clusters.size() == 0 ) return;
				StringBuffer clusterNames = new StringBuffer();
				for (Cluster cluster:clusters){
					clusterNames.append(cluster.getClusterName());
				}
				
				detail.setDefaultModelObject(CommonValues.INFO_UPLOAD_COMPLETED + clusterNames.toString() );
				paramGrid.setVisible(false);
				resultGrid.setVisible(false);
				detail.setVisible(true);
				target.add(paramContainer);
				target.add(resultContainer);
				target.add(border);
				target.add(group);
				
			}});
		
		form.add(new AjaxButton("init"){
			private static final long serialVersionUID = 1L;
			protected void onAfterSubmit(AjaxRequestTarget target, Form<?> form) {
				
				if (result != null && !result.equalsIgnoreCase(""))
					detail.setDefaultModelObject(result);
				else
					detail.setDefaultModelObject(CommonValues.INFO_CREATE_COMPLETED );
				
				paramGrid.setVisible(false);
				resultGrid.setVisible(false);
				detail.setVisible(true);
				target.add(paramContainer);
				target.add(resultContainer);
				target.add(border);
				target.add(group);
				
			}});

		form.add(new Button("modify"));
		
		// add refresh
		form.add(new AjaxLink<Object>("refresh"){
			private static final long serialVersionUID = 1L;
			public void onClick(AjaxRequestTarget target) {
				DefaultImpl.getFuncList(appNo,funcs);	
				target.add(group);
			}
		});		
	
	}

	/*
	 * upload files to cluster available to app no
	 */
	private int upLoad(FuncBean func) {
		
		// write content to local file
		int ret = 0;
		
		String localTableFile = CommonValues.getTempDir()+"/"+ CommonValues.getFuncTableFileName(func.getFuncNo());
		ret = CommonUtils.writeFileToLocal(localTableFile, func.getTableFile());
		
		String localHandlerFile = CommonValues.getTempDir()+"/"+ CommonValues.getFuncHandlerFileName(func.getFuncNo());
		ret = CommonUtils.writeFileToLocal(localHandlerFile, func.getHandlerFile());	
		
		files.clear();
		files.add(localTableFile);
		files.add(localHandlerFile);
		
		// upload to hadoop 
		
			// get app available hadoop 
			DefaultImpl.getAppCluster(appNo, clusters);
			
			if (clusters.size() == 0) { info(CommonValues.INFO_APP_NO_CLUSTER); return -1 ;}
			
			for(Cluster cluster:clusters){
				
				RemoteShellExec remote = new RemoteShellExec(cluster.getMasterIp(),cluster.getSysUser(),cluster.getSysUserPassword());
				try {
					remote.login();
					
					// upload scripts file
					remote.scpFile(files, cluster.getScriptPath());
					
					remote.close();
					
				} catch (IOException e) {
					log.error(e.getMessage());
					return -1;
				}
								
			}
		
		return ret;
	}
	
	/*
	 * init func by creating tables in clusters
	 */
	private int init(FuncBean func) {
		
		int ret = 0;
		flag = true;
		String tableFile = CommonValues.getFuncTableFileName(func.getFuncNo());
		
		// create tables on cluster available to app no for this func no 
		
			// get app available hadoop 
			DefaultImpl.getAppCluster(appNo, clusters);
			
			if (clusters.size() == 0) { info(CommonValues.INFO_APP_NO_CLUSTER); return -1 ;}
			
			for(Cluster cluster:clusters){
				
				RemoteShellExec remote = new RemoteShellExec(cluster.getMasterIp(),cluster.getSysUser(),cluster.getSysUserPassword());
				try {
					remote.login();
					
					// Create tables
					result = remote.exec("sh " + cluster.getScriptPath() + "/" + tableFile + " " + func.getFuncNo());
					remote.close();
					
					if (!result.equalsIgnoreCase("")) return -1;
					
				} catch (IOException e) {
					log.error(e.getMessage());
					return -1;
				}
								
			}
		flag = false;
		return ret;
	}


}
